﻿using System.Diagnostics.CodeAnalysis;
using System.Windows.Input;
using System.Collections.Generic;

namespace System.ApplicationModel
{
    /// <summary>
    /// A <see cref="System.Windows.Input.ICommand"/> based on delegates.
    /// </summary>
    public class DelegateCommand : ICommand
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DelegateCommand"/> class.
        /// </summary>
        /// <param name="executeMethod">The execution method.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="executeMethod"/> is null.</exception>
        public DelegateCommand(Action executeMethod)
            : this(executeMethod, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DelegateCommand"/> class.
        /// </summary>
        /// <param name="executeMethod">The execution method.</param>
        /// <param name="canExecuteMethod">The execution control method.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="executeMethod"/> is null.</exception>
        public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
        {
            if (executeMethod == null)
            {
                throw new ArgumentNullException("executeMethod");
            }

            _executeMethod = executeMethod;
            _canExecuteMethod = canExecuteMethod;
        }
        #endregion

        #region Fields
        private readonly Action _executeMethod = null;
        private readonly Func<bool> _canExecuteMethod = null;
        #endregion

        #region Public Members
        /// <summary>
        /// Execute the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <returns>true if this command can be executed; otherwise, false.</returns>
        public bool CanExecute()
        {
            return _canExecuteMethod == null ? true : _canExecuteMethod();
        }

        /// <summary>
        /// Execute the command
        /// </summary>
        public void Execute()
        {
            _executeMethod();
        }
        
#if !SILVERLIGHT
        /// <summary>
        /// Occurs when changes occur that affect whether or not the command should execute.
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }
#else
        /// <summary>
        /// Occurs when the return value of the control method has changed.
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Raises the <see cref="CanExecuteChanged"/> event.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
        public void RaiseCanExecuteChanged()
        {
            OnCanExecuteChanged();
            var handler = CanExecuteChanged;
            if (handler != null) handler(this, EventArgs.Empty);
        }

        /// <summary>
        /// Occurs when the <see cref="CanExecuteChanged"/> event is raised.
        /// </summary>
        protected virtual void OnCanExecuteChanged()
        {

        }
#endif
        #endregion

        #region ICommand Implementation
        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
        /// <returns>true if this command can be executed; otherwise, false.</returns>
        bool ICommand.CanExecute(object parameter)
        {
            if (object.ReferenceEquals(_canExecuteMethod, null))
                return true;
            return _canExecuteMethod();
        }

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
        void ICommand.Execute(object parameter)
        {
            _executeMethod();
        }
        #endregion
    }
}
